
#include <BPainter>
#include <BSystem>
#include <BMouseEvent>
#include <BKeyEvent>
#include <BOpenGL>
#include <BHBoxLayout>
#include <BVBoxLayout>
#include <BGridLayout>
#include <BStackLayout>

#include "PixmapArea.h"

PixmapArea::PixmapArea(BWidget* parent)
{
	this->setStyleSheet("background:36, 36, 36");

	this->setAreaAlign(Align_Center);

	_pixmap = new BPixmap();
	_pixmap->setFilter(BTexture::Filter_Nearest);

	_hoverPoint.set(-1, -1);
	_hoveredObject = 0;
	_dragPart = Part_None;
	_dirtySelection = false;

	connect(_pixmap, Signal_Dirty, &PixmapArea::slotPixmapDirty);
	connect(_pixmap, Signal_Framed, &PixmapArea::slotPixmapFramed);
	connect(_pixmap, Signal_Resized, &PixmapArea::slotPixmapResized);

}
PixmapArea::~PixmapArea()
{

}

BPixmap* PixmapArea::pixmap() const
{
	return _pixmap;
}

void PixmapArea::enableEvent()
{
	_eventEnabled = true;
}
void PixmapArea::disableEvent()
{
	_eventEnabled = false;
}
bool PixmapArea::eventEnabled()
{
	return 	_eventEnabled;
}

void PixmapArea::select(BObject* object)
{
	if (object && !_selectedObjects.contain(object))
	{
		_selectedObjects.insert(object);
		_dirtySelection = true;
		this->fresh();
		emit("selected", object);
	}
}
void PixmapArea::unselect(BObject* object)
{
	if (_selectedObjects.remove(object))
	{
		_dirtySelection = true;
		this->fresh();
		emit("unselected", object);
	}
}
void PixmapArea::clearSelection()
{
	if (_selectedObjects.size())
	{
		while (BObject* object = _selectedObjects.last())
		{
			_selectedObjects.remove(_selectedObjects.size() - 1);
			emit("unselected", object);
		}
		_dirtySelection = true;
		this->fresh();
	}
}

void PixmapArea::adjustArea()
{
	BSize size = _pixmap->size();
	BRect area = this->area();
	if (this->area().size() != size)
	{
		area.setSize(size);
		this->setArea(area);
	}
}
void PixmapArea::applyPartDraging(const BPoint& pos)
{
	BPoint offset = pos - _pressPoint;
	BRect initRect = _dragRectMap[Part_None];
	if (_dragPart == Part_Center)
	{
		for (int i = 0; i < _selectedObjects.size(); i++)
		{
			BObject* object = _selectedObjects[i];
			BRect newRect = _dragInitialRects[i];
			newRect += offset;
			if (BWidget* widget = dynamic_cast<BWidget*>(object))
				widget->setRect(newRect);
			else if (BLayout* layout = dynamic_cast<BLayout*>(object))
				layout->setRect(newRect);
		}
		return;
	}
	if (_dragPart == Part_Left)
	{
		BRect rect = initRect;
		rect.left() = rect.left() + offset.x();
		BPoint origin = initRect.rightCenter() - this->area().pos();
		double ratio = (double)rect.width() / initRect.width();
		for (int i = 0; i < _selectedObjects.size(); i++)
		{
			BObject* object = _selectedObjects[i];
			BRect newRect = _dragInitialRects[i];
			newRect.scale(origin, ratio, 1.0);
			if (BWidget* widget = dynamic_cast<BWidget*>(object))
				widget->setRect(newRect);
			else if (BLayout* layout = dynamic_cast<BLayout*>(object))
				layout->setRect(newRect);
		}
		return;
	}
	if (_dragPart == Part_Top)
	{
		BRect rect = initRect;
		rect.top() += offset.y();
		BPoint origin = initRect.bottomCenter() - this->area().pos();
		double ratio = (double)rect.height() / initRect.height();
		for (int i = 0; i < _selectedObjects.size(); i++)
		{
			BObject* object = _selectedObjects[i];
			BRect newRect = _dragInitialRects[i];
			newRect.scale(origin, 1.0, ratio);
			if (BWidget* widget = dynamic_cast<BWidget*>(object))
				widget->setRect(newRect);
			else if (BLayout* layout = dynamic_cast<BLayout*>(object))
				layout->setRect(newRect);
		}
		return;
	}
	if (_dragPart == Part_Right)
	{
		BRect rect = initRect;
		rect.right() += offset.x();
		BPoint origin = initRect.leftCenter() - this->area().pos();
		double ratio = (double)rect.width() / initRect.width();
		for (int i = 0; i < _selectedObjects.size(); i++)
		{
			BObject* object = _selectedObjects[i];
			BRect newRect = _dragInitialRects[i];
			newRect.scale(origin, ratio, 1.0);
			if (BWidget* widget = dynamic_cast<BWidget*>(object))
				widget->setRect(newRect);
			else if (BLayout* layout = dynamic_cast<BLayout*>(object))
				layout->setRect(newRect);
		}
		return;
	}
	if (_dragPart == Part_Bottom)
	{
		BRect rect = initRect;
		rect.bottom() += offset.y();
		BPoint origin = initRect.topCenter() - this->area().pos();
		double ratio = (double)rect.height() / initRect.height();
		for (int i = 0; i < _selectedObjects.size(); i++)
		{
			BObject* object = _selectedObjects[i];
			BRect newRect = _dragInitialRects[i];
			newRect.scale(origin, 1.0, ratio);
			if (BWidget* widget = dynamic_cast<BWidget*>(object))
				widget->setRect(newRect);
			else if (BLayout* layout = dynamic_cast<BLayout*>(object))
				layout->setRect(newRect);
		}
		return;
	}
	if (_dragPart == Part_LeftTop)
	{
		BRect rect = initRect;
		rect.left() += offset.x();
		rect.top() += offset.y();
		BPoint origin = initRect.rightBottom() - this->area().pos();
		double wratio = (double)rect.width() / initRect.width();
		double hRatio = (double)rect.height() / initRect.height();
		for (int i = 0; i < _selectedObjects.size(); i++)
		{
			BObject* object = _selectedObjects[i];
			BRect newRect = _dragInitialRects[i];
			newRect.scale(origin, wratio, hRatio);
			if (BWidget* widget = dynamic_cast<BWidget*>(object))
				widget->setRect(newRect);
			else if (BLayout* layout = dynamic_cast<BLayout*>(object))
				layout->setRect(newRect);
		}
		return;
	}
	if (_dragPart == Part_LeftBottom)
	{
		BRect rect = initRect;
		rect.left() += offset.x();
		rect.bottom() += offset.y();
		BPoint origin = initRect.rightTop() - this->area().pos();
		double wratio = (double)rect.width() / initRect.width();
		double hRatio = (double)rect.height() / initRect.height();
		for (int i = 0; i < _selectedObjects.size(); i++)
		{
			BObject* object = _selectedObjects[i];
			BRect newRect = _dragInitialRects[i];
			newRect.scale(origin, wratio, hRatio);
			if (BWidget* widget = dynamic_cast<BWidget*>(object))
				widget->setRect(newRect);
			else if (BLayout* layout = dynamic_cast<BLayout*>(object))
				layout->setRect(newRect);
		}
		return;
	}
	if (_dragPart == Part_RightTop)
	{
		BRect rect = initRect;
		rect.right() += offset.x();
		rect.top() += offset.y();
		BPoint origin = initRect.leftBottom() - this->area().pos();
		double wratio = (double)rect.width() / initRect.width();
		double hRatio = (double)rect.height() / initRect.height();
		for (int i = 0; i < _selectedObjects.size(); i++)
		{
			BObject* object = _selectedObjects[i];
			BRect newRect = _dragInitialRects[i];
			newRect.scale(origin, wratio, hRatio);
			if (BWidget* widget = dynamic_cast<BWidget*>(object))
				widget->setRect(newRect);
			else if (BLayout* layout = dynamic_cast<BLayout*>(object))
				layout->setRect(newRect);
		}
		return;
	}
	if (_dragPart == Part_RightBottom)
	{
		BRect rect = initRect;
		rect.right() += offset.x();
		rect.bottom() += offset.y();
		BPoint origin = initRect.leftTop() - this->area().pos();
		double wratio = (double)rect.width() / initRect.width();
		double hRatio = (double)rect.height() / initRect.height();
		for (int i = 0; i < _selectedObjects.size(); i++)
		{
			BObject* object = _selectedObjects[i];
			BRect newRect = _dragInitialRects[i];
			newRect.scale(origin, wratio, hRatio);
			if (BWidget* widget = dynamic_cast<BWidget*>(object))
				widget->setRect(newRect);
			else if (BLayout* layout = dynamic_cast<BLayout*>(object))
				layout->setRect(newRect);
		}
		return;
	}
}
void PixmapArea::paintLayout(BWidget* widget, BPainter& painter)
{
	for (int i = 0; i < widget->layoutCount(); i++)
	{
		BLayout* layout = widget->layout(i);
		paintLayout(layout, painter);
	}
}
void PixmapArea::paintLayout(BLayout* layout, BPainter& painter)
{
	BRect layoutRect = layout->rect() + this->offset() + layout->host()->pos();
	painter.setColor(200, 0, 0);
	painter.drawRect(layoutRect);
	if (BHBoxLayout* hlayout = dynamic_cast<BHBoxLayout*>(layout))
	{
		for (int i = 0; i < hlayout->count(); i++)
		{
			if (BLayout* child = hlayout->layout(i))
				paintLayout(child, painter);
		}
		return;
	}
	if (BVBoxLayout* vlayout = dynamic_cast<BVBoxLayout*>(layout))
	{
		for (int i = 0; i < vlayout->count(); i++)
		{
			if (BLayout* child = vlayout->layout(i))
				paintLayout(child, painter);
		}
		return;
	}
	if (BStackLayout* slayout = dynamic_cast<BStackLayout*>(layout))
	{
		for (int i = 0; i < slayout->count(); i++)
		{
			if (BLayout* child = slayout->layout(i))
				paintLayout(child, painter);
		}
		return;
	}
	if (BGridLayout* gridLayout = dynamic_cast<BGridLayout*>(layout))
	{
		for (int r = 0; r < gridLayout->rowCount(); r++)
		{
			for (int c = 0; c < gridLayout->columnCount(); c++)
			{
				BRect rect = gridLayout->cellRect(r, c) + this->offset() + layout->host()->pos();
				painter.setColor(200, 0, 200);
				painter.drawRect(rect);
				if (BLayout* child = gridLayout->layout(r, c))
					paintLayout(child, painter);
			}
		}
		return;
	}
}

void PixmapArea::slotPixmapResized()
{
	adjustArea();
}
void PixmapArea::slotPixmapDirty()
{
	this->fresh();
}
void PixmapArea::slotPixmapFramed()
{

}

void PixmapArea::scrolling(Orientation orientation)
{
	this->fresh();
}

void PixmapArea::event(const BEvent& event)
{
	if (_eventEnabled)
	{
		if (event.type() >= Event_MouseMove && event.type() <= Event_DoubleClick)
		{
			BMouseEvent mouseEvent(event);
			BPoint point = mouseEvent.pos() - this->area().pos();
			mouseEvent.setGlobalPos(point);
			mouseEvent.setAbsolutePos(point);
			_pixmap->event(mouseEvent);
		}
		else
		{
			_pixmap->event(event);
		}
	}
	else
	{
		if (event.type() == Event_Update || event.type() == Event_Fresh)
		{
			_pixmap->event(event);
		}
		BScrollArea::event(event);
	}
	if (event.type() == Event_Paint)
	{
		BScrollArea::event(event);
	}
}

void PixmapArea::freshEvent(const BEvent& event)
{
	BScrollArea::freshEvent(event);
	BRect area = this->area();
	float sw = area.width() / (float)_pixmap->width();
	float sh = area.height() / (float)_pixmap->height();
	if (_dirtySelection)
	{
		if (_selectedObjects.size())
		{
			BRect rect;
			BObject* first = _selectedObjects.min();
			if (BWidget* widget = dynamic_cast<BWidget*>(first))
				rect = widget->globalRect() + area.pos();
			else if (BLayout* layout = dynamic_cast<BLayout*>(first))
				rect = layout->rect() + layout->host()->globalPos() + area.pos();
			for (int i = 1; i < _selectedObjects.size(); i++)
			{
				if (BWidget* widget = dynamic_cast<BWidget*>(first))
					rect.expand(widget->globalRect() + area.pos());
				else if (BLayout* layout = dynamic_cast<BLayout*>(first))
					rect.expand(layout->rect() + layout->host()->globalPos() + area.pos());
			}
			int b = 5;
			_dragRectMap[Part_None] = rect;
			_dragRectMap[Part_Left] = BRect(-b, b, b + b, rect.height() - b - b) + rect.pos();
			_dragRectMap[Part_Top] = BRect(b, -b, rect.width() - b -b, b + b) + rect.pos();
			_dragRectMap[Part_Right] = BRect(rect.width() - b, b, b + b, rect.height() - b - b) + rect.pos();
			_dragRectMap[Part_Bottom] = BRect(b, rect.height() - b, rect.width() - b - b, b + b) + rect.pos();
			_dragRectMap[Part_Center] = BRect(b, b, rect.width() - b - b, rect.height() - b - b) + rect.pos();
			_dragRectMap[Part_LeftTop] = BRect(-b, -b, b + b, b + b) + rect.pos();
			_dragRectMap[Part_LeftBottom] = BRect(-b, rect.height() - b, b + b, b + b) + rect.pos();
			_dragRectMap[Part_RightTop] = BRect(rect.width() - b, -b, b + b, b + b) + rect.pos();
			_dragRectMap[Part_RightBottom] = BRect(rect.width() - b, rect.height() - b, b + b, b + b) + rect.pos();
		}
		else
		{
			_dragRectMap.reset();
		}
		_dirtySelection = false;
	}
}
void PixmapArea::paintEvent(const BEvent& event)
{
	BPainter painter(this);
	painter.drawBackground();
	painter.drawPerch();
	painter.drawMargin();
	painter.drawBorder();

	const BRect& crect = this->centerRect();
	painter.setClip(crect);

	BRect area = this->area();
	painter.setColor(Color_Text);
	painter.drawRect(area);

	painter.setColor(255, 255, 255);
	painter.fillRect(this->area());

	painter.bind(_pixmap);
	painter.fillRect(this->area());
	painter.unbind();

	for (int i = 0; i < _pixmap->widgetCount(); i++)
	{
		BWidget* widget = _pixmap->widget(i);
		paintLayout(widget, painter);
	}

	if (_enterDrag)
	{
		painter.setColor(255, 200, 156, 128);
		painter.fillRect(_enterDragRect);
	}

	if (_hoverPoint > BPoint(-1, -1))
	{
		float sw = area.width() / (float)_pixmap->width();
		float sh = area.height() / (float)_pixmap->height();
		int x = int(sw * _hoverPoint.x()) + area.x();
		int y = int(sh * _hoverPoint.y()) + area.y();
		painter.setColor(255, 0, 0);
		if (sw > 1 && sh > 1)
			painter.drawRect(x, y, (int)sw, (int)sh);
		else
			painter.drawPoint(x, y);
	}

	if (_hoveredObject)
	{
		if (BWidget* widget = dynamic_cast<BWidget*>(_hoveredObject))
		{
			BRect rect = widget->globalRect() + area.pos();
			painter.setLineStipple(0x0F0F);
			painter.setColor(Color_Border);
			painter.drawRect(rect);
			painter.setLineStipple(0);
		}
		if (BLayout* layout = dynamic_cast<BLayout*>(_hoveredObject))
		{
			BRect rect = layout->rect() + layout->host()->globalPos() + area.pos();
			painter.setLineStipple(0x0F0F);
			painter.setColor(Color_Border);
			painter.drawRect(rect);
			painter.setLineStipple(0);
		}

	}

	if (_selectedObjects.size())
	{
		painter.setColor(0, 200, 0);
		painter.drawRect(_dragRectMap[Part_None]);
		painter.setLineStipple(0xFF00);
		for (int i = 0; i < _selectedObjects.size(); i++)
		{
			BObject* object = _selectedObjects[i];
			if (BWidget* widget = dynamic_cast<BWidget*>(object))
			{
				BRect rect = widget->globalRect() + area.pos();
				rect.left() += 1;
				rect.bottom() -= 1;
				painter.setColor(Color_Border, State_Checked);
				painter.drawRect(rect);
			}
			if (BLayout* layout = dynamic_cast<BLayout*>(_hoveredObject))
			{
				BRect rect = layout->rect() + layout->host()->globalPos() + area.pos();
				rect.left() += 1;
				rect.bottom() -= 1;
				painter.setColor(Color_Border, State_Checked);
				painter.drawRect(rect);
			}
		}
		painter.setLineStipple(0);
	}
}

void PixmapArea::keyPressEvent(const BKeyEvent& event)
{
	if (event.key() == Key_Delete)
	{
		if (_selectedObjects.size())
		{
			//RemoveOpera* opera = new RemoveOpera();
			for (int i = 0; i < _selectedObjects.size(); i++)
			{
				BObject* object = _selectedObjects[i];
				if (BWidget* widget = dynamic_cast<BWidget*>(object))
				{
					_pixmap->removeWidget(widget);
					continue;
				}
				if (BLayout* layout = dynamic_cast<BLayout*>(object))
				{
					BWidget* host = layout->host();
					host->removeLayout(layout);
				}
				//opera->addLayer(object);
			}
			//emit("opera", opera);
			event.accept();
		}
	}
	BScrollArea::keyPressEvent(event);
}

void PixmapArea::mouseEnterEvent(const BMouseEvent& event)
{
	if (_hoverPoint != BPoint(-1, -1))
	{
		_hoverPoint.set(-1, -1);
		this->fresh();
	}
}
void PixmapArea::mouseLeaveEvent(const BMouseEvent& event)
{
	if (_hoverPoint != BPoint(-1, -1))
	{
		_hoverPoint.set(-1, -1);
		this->fresh();
	}
}

void PixmapArea::mousePressEvent(const BMouseEvent& event)
{
	if (event.button() == Button_Left)
	{
		_pressPoint = event.pos();
		if (_dragPart)
		{
			_draging = true;
			_dragInitialRects.resize(_selectedObjects.size());
			for (int i = 0; i < _selectedObjects.size(); i++)
			{
				BObject* object = _selectedObjects[i];
				if (BWidget* widget = dynamic_cast<BWidget*>(object))
					_dragInitialRects[i] = widget->rect();
				else if (BLayout* layout = dynamic_cast<BLayout*>(object))
					_dragInitialRects[i] = layout->rect();
			}
			return;
		}
		if (!event.keyState(Key_LCtrl))
		{
			for (int i = 0; i < _selectedObjects.size(); i++)
			{
				BObject* object = _selectedObjects[i];
				if (object == _hoveredObject)
					continue;
				_selectedObjects.remove(i--);
				emit("unselected", object);
			}
		}
		if (_hoveredObject)
		{
			select(_hoveredObject);
		}
		this->fresh();
		return;
	}
	if (event.button() == Button_Middle)
	{
		_draging = true;
		_pressPoint = event.pos();
		_pressArea = this->area();
		this->setCursor(Cursor_SizeAll);
	}
}
void PixmapArea::mouseReleaseEvent(const BMouseEvent& event)
{
	if (_draging)
	{
		_draging = false;
		this->setCursor(Cursor_None);
		if (event.button() == Button_Middle)
		{

		}
		if (event.button() == Button_Left)
		{
			_dragPart = Part_None;
			_dragInitialRects.clear();
			_dirtySelection = true;
		}
	}
}
void PixmapArea::mouseMoveEvent(const BMouseEvent& event)
{
	const BPoint& pos = event.pos();
	if (_draging)
	{
		if (_dragPart)
		{
			applyPartDraging(pos);
			this->fresh();
			return;
		}
		BRect area = _pressArea + (pos - _pressPoint);
		this->setArea(area);
		return;
	}
	if (event.button() == Button_Left)
	{
		if (_hoveredObject && (pos - _pressPoint).length() > 3)
		{
			//BDragHolder drag = new BDrag();
			//drag->setValue(_hoveredObject);
			//int ret = drag->execute();
		}
		return;
	}
	const BRect& area = this->area();
	BWidget* hoveredLayer = (BWidget*)_pixmap->widgetAt(pos - area.pos());
	if (_hoveredObject != hoveredLayer)
	{
		_hoveredObject = hoveredLayer;
		this->fresh();
	}
	_dragPart = Part_None;
	if (event.keyState(Key_LCtrl) && (!_hoveredObject || _selectedObjects.contain(_hoveredObject)))
	{
		for (auto it = _dragRectMap.begin(); it < _dragRectMap.end(); it++)
		{
			if (it.key() > Part_None && it->contain(pos))
			{
				_dragPart = it.key();
				break;
			}
		}
	}
	if (_dragPart == Part_None)
		this->setCursor(Cursor_None);
	else if (_dragPart == Part_Center)
		this->setCursor(Cursor_SizeAll);
	else if (_dragPart == Part_Left || _dragPart == Part_Right)
		this->setCursor(Cursor_SizeHor);
	else if (_dragPart == Part_Top || _dragPart == Part_Bottom)
		this->setCursor(Cursor_SizeVer);
	else if (_dragPart == (Part_Left | Part_Top))
		this->setCursor(Cursor_SizeLeftTop);
	else if (_dragPart == (Part_Left | Part_Bottom))
		this->setCursor(Cursor_SizeLeftBottom);
	else if (_dragPart == (Part_Right | Part_Top))
		this->setCursor(Cursor_SizeRightTop);
	else if (_dragPart == (Part_Right | Part_Bottom))
		this->setCursor(Cursor_SizeRightBottom);

	if (area.contain(pos))
	{
		BPoint point(-1, -1);
		point.x() = int((pos.x() - area.x()) / (float)area.width() * _pixmap->width());
		point.y() = int((pos.y() - area.y()) / (float)area.height() * _pixmap->height());
		if (_hoverPoint != point)
		{
			_hoverPoint = point;
			this->fresh();
		}
	}
}

void PixmapArea::dragEnterEvent(const BMouseEvent& event)
{
	_enterDrag = (BDrag*)event.value();
	_enterDragRect.reset();
	_enterHoverObject = 0;
	this->fresh();
}
void PixmapArea::dragLeaveEvent(const BMouseEvent& event)
{
	_enterDrag = 0;
	_enterDragRect.reset();
	_enterHoverObject = 0;
	this->fresh();
}
void PixmapArea::dragMoveEvent(const BMouseEvent& event)
{
	const BPoint& pos = event.pos();
	_enterDragRect.set(pos - 25, BSize(50));
	this->fresh();
}
void PixmapArea::dragDropEvent(const BMouseEvent& event)
{
	_enterDrag = 0;
	this->fresh();
}
